#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include "Log.hpp"
#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <queue>

#define SIZE 1024

class UdpServer
{
private:
    // ip和port
    std::string _ip;
    uint16_t _port;
    int _socketFd;

public:
    // 这里填空串 下面三目运算分配INADDR_ANY;填指定串 server以指定串作为ip
    UdpServer(uint16_t port, std::string ip = "0.0.0.0")
        : _ip(ip),
          _port(port),
          _socketFd(-1)
    {
    }

    bool initServer()
    {
        // 新系统调用完成网络功能

        // 1. socket创建套接字 协议用IPv4 套接字类型用数据报套接字
        _socketFd = socket(AF_INET, SOCK_DGRAM, 0); // #define AF_INET PF_INET 二者相同
        if (_socketFd < 0)
        {
            logMsg(FATAL, "socket::%d:%s", errno, strerror(errno));
            exit(2); // 正规写法：规定每个退出码代表什么意思
        }

        // 2. bind绑定端口号 将一个套接字绑定到一个特定的ip和port

        struct sockaddr_in svr_sockAddr;
        bzero(&svr_sockAddr, sizeof(svr_sockAddr));

        svr_sockAddr.sin_family = AF_INET;
        // #define INADDR_ANY ((in_addr_t) 0x00000000) 字符串--32位整数--网络字节序
        svr_sockAddr.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        svr_sockAddr.sin_port = htons(_port); // 主机字节序--网络字节序

        // int bind(int __fd, const sockaddr *__addr, socklen_t __len)
        if (bind(_socketFd, (struct sockaddr *)&svr_sockAddr, sizeof(svr_sockAddr)) < 0)
        {
            logMsg(FATAL, "bind::%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMsg(NORMAL, "init udp server done ... %s", strerror(errno));
        return true;
    }

    void Start()
    {
        // 响应式服务器:原封地不动返回client发送的消息
        char buffer[SIZE];
        while (true)
        {
            // clt_sockAddr 纯输出型参数
            struct sockaddr_in clt_sockAddr;
            bzero(&clt_sockAddr, sizeof(clt_sockAddr));

            // clint_addrlen: 输入输出型参数
            socklen_t clint_addrlen = sizeof(clt_sockAddr); // unsigned int

            // 读取数据
            ssize_t bytes_read = recvfrom(_socketFd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&clt_sockAddr, &clint_addrlen);
            if (bytes_read == -1)
            {
                logMsg(FATAL, "recvfrom::%d:%s", errno, strerror(errno));
                exit(3);
            }

            // 谁 发送的 什么信息
            if (bytes_read > 0)
            {
                buffer[bytes_read] = 0; // 数据当做字符串使用

                uint16_t client_port = ntohs(clt_sockAddr.sin_port);   // 网络字节序 --> 主机字节序
                std::string cli_ip = inet_ntoa(clt_sockAddr.sin_addr); // 4字节的网络序列的IP->本主机字符串风格的IP 存在线程安全问题

                printf("[%s:%d] to me[server]# %s\n", cli_ip.c_str(), client_port, buffer);
            }

            // 回显数据
            sendto(_socketFd, buffer, strlen(buffer), 0, (struct sockaddr *)&clt_sockAddr, clint_addrlen);
        }
    }

    ~UdpServer()
    {
        if (_socketFd >= 0)
            close(_socketFd);
    }
};

#endif
